package service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import queries.BandQueries;
import queries.EventQueries;
import queries.VenueQueries;
import util.LollaException;
import android.content.Context;
import android.dataaccess.LollaDbAdapter;
import android.database.SQLException;
import android.util.Log;
import controller.LollaWrapper;
import entities.Band;
import entities.Event;
import entities.LollaObject;
import entities.Update;
import entities.Venue;

public class LollaServiceImpl implements LollaService {

	private LollaWrapper wrapper;
	private LollaDbAdapter dbAdapter;

	public LollaServiceImpl(Context ctx) {
		wrapper = new LollaWrapper(util.Constants.API_KEY);
		dbAdapter = new LollaDbAdapter(ctx);
	}

	@Override
	public LollaObject getByName(String name, Class<? extends LollaObject> type) {
		dbAdapter.open();
		LollaObject ret = dbAdapter.getByName(name, type);
		dbAdapter.close();
		return ret;
	}

	@Override
	public LollaObject getById(long id, Class<? extends LollaObject> type) {
		dbAdapter.open();
		LollaObject ret = dbAdapter.getById(id, type);
		dbAdapter.close();
		return ret;
	}

	@Override
	public List<? extends LollaObject> getAllByType(
			Class<? extends LollaObject> type) {
		dbAdapter.open();
		List<? extends LollaObject> ret = dbAdapter.getAllByType(type);
		dbAdapter.close();
		return ret;
	}

	@Override
	public long countType(Class<? extends LollaObject> type) {
		dbAdapter.open();
		long ret = dbAdapter.countType(type);
		dbAdapter.close();
		return ret;
	}

	@Override
	public List<Band> getBandsByEvent(Event event) {
		dbAdapter.open();
		List<Band> ret = dbAdapter.getBandsByEvent(event);
		dbAdapter.close();
		return ret;
	}

	@Override
	public List<Event> getEventsByBand(Band band) {
		dbAdapter.open();
		List<Event> ret = dbAdapter.getEventsByBand(band);
		dbAdapter.close();
		return ret;
	}

	@Override
	public List<Event> getEventsByVenue(Venue venue) {
		dbAdapter.open();
		List<Event> ret = dbAdapter.getEventsByVenue(venue);
		dbAdapter.close();
		return ret;
	}

	@Override
	public Venue getVenueByEvent(Event event) {
		dbAdapter.open();
		Venue ret = dbAdapter.getVenueByEvent(event);
		dbAdapter.close();
		return ret;
	}

	@Override
	public void save(LollaObject entity) {
		try {
			dbAdapter.open();
			dbAdapter.save(entity);
		} catch (SQLException e) {
			Log.e("LollaService", "Could not save entity " + entity.getTitle());
		} finally {
			dbAdapter.close();
		}
	}
	
	@Override
	public void saveOrUpdate(LollaObject entity) {
		try {
			dbAdapter.open();
			dbAdapter.saveOrUpdate(entity);
		} catch (SQLException e) {
			Log.e("LollaService", "Could not save or update entity " + entity.getTitle());
		} finally {
			dbAdapter.close();
		}
	}
	
	@Override
	public void saveOrUpdateAll(List<LollaObject> objects) {
		try {
			dbAdapter.open();
			dbAdapter.saveOrUpdateAll(objects);
		} catch (SQLException e) {
			Log.e("LollaService", "Could not save or update entities");
		} finally {
			dbAdapter.close();
		}
	}

	@Override
	public void saveAll() {
		List<Thread> syncThreads = new ArrayList<Thread>();
		syncThreads.add(new BandsSyncThread());
		syncThreads.add(new EventsSyncThread());
		syncThreads.add(new VenuesSyncThread());
		dbAdapter.open();
		for (Thread t : syncThreads)
			t.start();
		for (Thread t : syncThreads) {
			try {
				t.join();
			} catch (InterruptedException e1) {
				throw new LollaException();
			}
		}
		dbAdapter.close();
		insertRelationships();
	}

	@Override
	public void update(Update update) {
		dbAdapter.open();
		dbAdapter.processUpdate(update);
		dbAdapter.close();
	}

	@Override
	public void updateAll(List<Update> updates) {
		dbAdapter.open();
		dbAdapter.processMultipleUpdate(updates);
		dbAdapter.close();
	}

	@Override
	public long addBandToMySchedule(Band band) {
		dbAdapter.open();
		long ret = dbAdapter.addBandToMySchedule(band);
		dbAdapter.close();
		return ret;
	}

	@Override
	public void removeBandFromMySchedule(Band band) {
		dbAdapter.open();
		dbAdapter.removeBandFromMySchedule(band);
		dbAdapter.close();
	}

	@Override
	public List<Band> getAllBandsFromMySchedule() {
		dbAdapter.open();
		List<Band> ret = dbAdapter.getAllBandsFromMySchedule();
		dbAdapter.close();
		return ret;
	}
	
	@Override
	public void buildAndSaveImages() {
		dbAdapter.open();
		dbAdapter.buildAndSaveImages();
		dbAdapter.close();
	}
	
	public class EventsSyncThread extends Thread {
		public void run() {
			saveAllEvents();
		}
	}
	
	public class BandsSyncThread extends Thread {
		public void run() {
			saveAllBands();
		}
	}
	
	public class VenuesSyncThread extends Thread {
		public void run() {
			saveAllVenues();
		}
	}
	
	@SuppressWarnings("unchecked")
	private void insertRelationships() {
		List<Event> events = (List<Event>) getAllByType(Event.class);
		try {
			dbAdapter.open();
			Log.i("LollaService", "Inserting relationship data");
			dbAdapter.insertRelationships(events);
			Log.i("LollaService", "Relationship data successfully inserted");
		} catch (SQLException e) {
			Log.e("LollaService", "Could not insert relationship data");
		} finally {
			dbAdapter.close();
		}
	}
	
	private void saveAllBands() {
		List<LollaObject> bands = new ArrayList<LollaObject>();
		try {
			Log.i("lollawrap4j", "Building Band query object");
			BandQueries bandQ = new BandQueries(wrapper);
			Log.i("lollawrap4j", "Band query object built successfully");
			Log.i("lollawrap4j", "Retrieving and building Bands from web service");
			bands.addAll(bandQ.getAllBands());
			Log.i("lollawrap4j", bands.size() + " Bands retrieved");
		} catch (IOException e) {
			throw new LollaException();
		}
		
		dbAdapter.saveOrUpdateAll(bands);
	}
	
	private void saveAllEvents() {
		List<LollaObject> events = new ArrayList<LollaObject>();
		try {
			Log.i("lollawrap4j", "Building Event query object");
			EventQueries eventQ = new EventQueries(wrapper);
			Log.i("lollawrap4j", "Event query object built successfully");
			Log.i("lollawrap4j", "Retrieving and building Events from web service");
			events.addAll(eventQ.getAllEvents());
			Log.i("lollawrap4j", events.size() + " Events retrieved");
		} catch (IOException e) {
			throw new LollaException();
		}
		
		dbAdapter.saveOrUpdateAll(events);
	}
	
	private void saveAllVenues() {
		List<LollaObject> venues = new ArrayList<LollaObject>();
		try {
			Log.i("lollawrap4j", "Building Venue query object");
			VenueQueries venueQ = new VenueQueries(wrapper);
			Log.i("lollawrap4j", "Venue query object built successfully");
			Log.i("lollawrap4j", "Retrieving and building Venues from web service");
			venues.addAll(venueQ.getAllVenues());
			Log.i("lollawrap4j", venues.size() + " Venues retrieved");
		} catch (IOException e) {
			throw new LollaException();
		}
		
		dbAdapter.saveOrUpdateAll(venues);
	}

}
